// --------------------------------
// <copyright file="PowerUp.cs" company="Faculty of Information Technology, Czech Technical University in Prague">
//     This document is shared by Microsoft Public License (MS-PL)
//     For further details please see http://msdn.microsoft.com/en-us/library/ff648068.aspx or license.txt.
// </copyright>
// <author>Vladislav Richter</author>

// <email>richtvl2@fit.cvut.cz</email>

// <date>9. 11. 2011 22:22:37</date>

// <summary>
//
// </summary>
// --------------------------------
using System;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using FarseerPhysics.Factories;
using FITClub.Episode1.Components;
using FITClub.Episode1.Controller.AI;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace FITClub.Episode1.Levels
{
    /// <summary>
    /// All types of PowerUps
    /// </summary>
    public enum PowerUpType : int { None, SuperSpeed, SlowSpeed, SuperJump, ControlSwitch, Invisibility, SuperPunch };

    /// <summary>
    /// This game component represents single PowerUp position - if the PowerUp is active, it draws rotating model of a question mark. If a player steps cloce to the PowerUp
    /// - it is deactivated for a while and sets random PowerUp effects on this player
    /// </summary>
    public class PowerUp : Microsoft.Xna.Framework.DrawableGameComponent, IConfigComponent
    {
        #region FIELDS

        /// <summary>
        /// Settings of this powerup
        /// </summary>
        public PowerUpConfig Config { get; set; }   /////////FAK!!!

        /// <summary>
        /// Model this component uses
        /// </summary>
        public Model PowerUpModel { get; private set; }

        public new GameFITClub Game { get; private set; }

        private Matrix[] transforms;
        private Matrix worldMatrix;
        /// <summary>
        /// Current model angle of rotation
        /// </summary>
        private float Angle = 0;
        private Vector3 Rotation = Vector3.Zero;
        /// <summary>
        /// Speed of rotation
        /// </summary>
        private float Speed = 30f;
        private bool loadPhysics;
        private PseudoRandom cryptoRandom = new PseudoRandom();
        private Random random = new Random(DateTime.Now.Millisecond + DateTime.Now.Second * DateTime.Now.Hour);
        /// <summary>
        /// This static variable keeps track of number of powerups currently active
        /// </summary>
        public static int PowerUpsOnScreen = 0;
        public float TimeToVisible;
        private double InvisibleTime;
        public double VisibilityTime;

        public Body Sensor;

        public PowerUpType NextType { get; set; }

        public event Action<PowerUp> Reset;

        int index;
        private int lastSequence;

        public int Index { get { return index; } }

        #endregion FIELDS

        #region METHODS

        public PowerUp(GameFITClub game, PowerUpConfig config, bool loadPhysics, int index)
            : base(game)
        {
            this.loadPhysics = loadPhysics;
            this.Config = config;
            this.Game = game;
            this.Visible = false;
            PowerUpsOnScreen = 0;
            this.index = index;
        }

        protected override void LoadContent()
        {
            base.LoadContent();
            if (loadPhysics)
            {
                Sensor = BodyFactory.CreateCircle(Game.PhysicsWorld, 0.15f, 1, "powerup");
                Sensor.SetTransform(new Vector2(this.Config.Transforms.Translation.X, this.Config.Transforms.Translation.Y), 0);
                Sensor.IsSensor = true;
                Sensor.OnCollision += SensorOnCollision;
            }
            this.ReloadConfig();
            this.ResetNext();
            Console.WriteLine("powerup loaded");
        }

        /// <summary>
        /// Detects if a player touches this powerup / if so it will give him an effect and reset itself
        /// </summary>
        public bool SensorOnCollision(Fixture f1, Fixture f2, Contact contact)
        {
            if (f2.Body.UserData != null)
            {
                ///Enum.GetNames(typeof(PowerUpType)).Length - 1
                (f2.Body.UserData as PlayableCharacter).SetPowerUp(NextType);
                this.Game.SoundPlayer.PlaySoundEffect("PowerUpTake", (f2.Body.UserData as PlayableCharacter).Position);
                PowerUpsOnScreen--;
                ResetNext();
            }
            return true;
        }

        public float GetRandomNumber(float minimum, float maximum)
        {
            return (float)(random.NextDouble() * (maximum - minimum) + minimum);
        }

        /// <summary>
        /// Deactivates this powerup and resets timers
        /// </summary>
        public void ResetNext()
        {
            this.Visible = false;
            this.InvisibleTime = 0;
            if (loadPhysics) this.Sensor.Enabled = false;
            this.TimeToVisible = GetRandomNumber((float)this.Config.MinTimeBeforeShow, (float)this.Config.MaxTimeBeforeShow);
            this.VisibilityTime = this.Config.ShowTime;
            NextType = (PowerUpType)(cryptoRandom.GetInt32(6)+1);
            
#if DEBUG
            System.Diagnostics.Debug.WriteLine("Powerup reset: {0}, timetovisible: {0}", NextType, TimeToVisible);
#endif
            if (this.Reset != null)
            {
                this.Reset(this);
            }
        }

        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
            if (this.Visible)
            {
                this.Config.Transforms.Rotation = new Vector3(this.Config.Transforms.Rotation.X, this.Config.Transforms.Rotation.Y + Speed * (float)gameTime.ElapsedGameTime.TotalSeconds, this.Config.Transforms.Rotation.Z);

                this.VisibilityTime -= gameTime.ElapsedGameTime.TotalSeconds;
                if (this.VisibilityTime <= 0.0)
                {
                    PowerUpsOnScreen--;
                    ResetNext();
                }
            }
            else
            {
                //this.TimeToVisible -= (float)gameTime.ElapsedGameTime.TotalSeconds;
                this.InvisibleTime += gameTime.ElapsedGameTime.TotalSeconds;
                if (this.TimeToVisible <= InvisibleTime)
                {
                    if (PowerUpsOnScreen < this.Config.MaxPowerUpsOnScreen)
                    {
                        PowerUpsOnScreen++;
                        this.Visible = true;
                        this.Sensor.Enabled = true;
                    }
                    else
                    {
                        ResetNext();
                    }
                }
            }
        }

        /// <summary>
        /// Draws this component
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Draw(GameTime gameTime)
        {
            // if (this.PowerUpModel != null)
            {
                this.UpdateWorld();
                foreach (ModelMesh mesh in this.PowerUpModel.Meshes)
                {
                    foreach (BasicEffect effect in mesh.Effects)
                    {
                        //effect.VertexColorEnabled = true;
                        effect.TextureEnabled = false;
                        effect.EnableDefaultLighting();
                        effect.PreferPerPixelLighting = true;
                        effect.World = this.transforms[mesh.ParentBone.Index] * this.worldMatrix;
                        effect.View = this.Game.ViewMatrix;
                        effect.Projection = this.Game.ProjectionMatrix;
                    }
                    mesh.Draw();
                }
            }
        }

        public void UpdateWorld()
        {
            this.worldMatrix = this.Config.Transforms.CreateTransform();
        }

        public void ReloadConfig()
        {
            this.UpdateWorld();
            this.PowerUpModel = this.Game.Content.Load<Model>(this.Config.ModelPath);
            this.transforms = new Matrix[this.PowerUpModel.Bones.Count];
            this.PowerUpModel.CopyAbsoluteBoneTransformsTo(this.transforms);

            ///// ---->>>>>>>>>>>>>>>>>>>>>>>> DOPLNIT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }

        #endregion METHODS

        internal void NextPowerUp(PowerUpType nextType, float timeToVisible, int seq)
        {
            if (seq > this.lastSequence)
            {
                this.lastSequence = seq;
                this.NextType = nextType;
                this.TimeToVisible = timeToVisible;
            }
        }
    }
}